# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Initialize fields on a grid, with a user-defined function
"""
from hysop.constants import Backend, Implementation
from hysop.fields.continuous_field import Field
from hysop.tools.htypes import check_instance, first_not_None, to_tuple
from hysop.tools.decorators import debug
from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors
from hysop.core.graph.computational_node_frontend import ComputationalGraphNodeFrontend
from hysop.core.graph.node_generator import ComputationalGraphNodeGenerator
from hysop.testsenv import __HAS_OPENCL_BACKEND__
[docs]
class AnalyticField(ComputationalGraphNodeGenerator):
"""
Applies an analytic formula, given by user, on all contained scalar fields.
Formula may be given in different formats depending on the
chosen implementation backend.
"""
@debug
def __new__(
cls,
field,
formula,
variables,
extra_input_kwds=None,
implementation=None,
base_kwds=None,
**kwds,
):
base_kwds = first_not_None(base_kwds, {})
return super().__new__(
cls,
candidate_input_tensors=None,
candidate_output_tensors=None,
**base_kwds,
)
@debug
def __init__(
self,
field,
formula,
variables,
extra_input_kwds=None,
implementation=None,
base_kwds=None,
**kwds,
):
"""
AnalyticField operator frontend.
Apply a user-defined formula onto a field, possibly
dependent on space variables and external fields/parameters.
Parameters
----------
field: hysop.field.continuous_field.Field
Continuous field to be modified.
formula : python function or sympy expression or tupe
The formula to be applied onto the scalar fields.
If the formula is a tuple of the length of the number of scalar fields,
fomula[component] is used instead.
variables: dict
Dictionary of fields as keys and topology descriptors as values.
implementation: Implementation, optional, defaults to None
target implementation, should be contained in available_implementations().
If None, implementation will be set to default_implementation().
extra_input_kwds: dict, optional
Extra inputs that will be forwarded to the formula.
Fields and Parameters are handled correctly as input requirements.
Only used for Implementation.PYTHON, discarded for other implementations.
base_kwds: dict, optional
Base class keyword arguments.
kwds: dict, optional
Extra parameters passed towards operator implementation.
"""
check_instance(field, Field)
check_instance(variables, dict, keys=Field, values=CartesianTopologyDescriptors)
extra_input_kwds = first_not_None(extra_input_kwds, {})
base_kwds = first_not_None(base_kwds, {})
assert "extra_input_kwds" not in kwds
assert "component" not in kwds
assert "coords" not in kwds
if (implementation is Implementation.PYTHON) and (extra_input_kwds is not None):
candidate_input_tensors = tuple(
filter(lambda f: isinstance(f, Field), extra_input_kwds.values())
)
else:
extra_input_kwds = {}
candidate_input_tensors = tuple()
candidate_output_tensors = (field,)
formula = to_tuple(formula)
if len(formula) == 1:
formula = formula * field.nb_components
check_instance(formula, tuple, size=field.nb_components)
super().__init__(
candidate_input_tensors=candidate_input_tensors,
candidate_output_tensors=candidate_output_tensors,
**base_kwds,
)
self._fields = field.fields
self._formula = formula
self._variables = variables
self._extra_input_kwds = extra_input_kwds
self._implementation = implementation
self._kwds = kwds
@debug
def _generate(self):
nodes = []
impl = self._implementation
variables = self._variables
assert len(self._formula) == len(self._fields)
for component, (formula, field) in enumerate(zip(self._formula, self._fields)):
if formula is None:
continue
kwds = self._kwds.copy()
extra_input_kwds = self._extra_input_kwds.copy()
extra_input_kwds["component"] = component
node = AnalyticScalarField(
field=field,
formula=formula,
variables=variables,
implementation=impl,
extra_input_kwds=extra_input_kwds,
**kwds,
)
nodes.append(node)
return nodes
[docs]
class AnalyticScalarField(ComputationalGraphNodeFrontend):
"""
Applies an analytic formula, given by user, on its field.
Formula may be given in different formats depending on the
chosen implementation backend.
"""
[docs]
@classmethod
def implementations(cls):
from hysop.backend.host.python.operator.analytic import PythonAnalyticField
__implementations = {
Implementation.PYTHON: PythonAnalyticField,
}
if __HAS_OPENCL_BACKEND__:
from hysop.backend.device.opencl.operator.analytic import (
OpenClAnalyticField,
)
__implementations.update({Implementation.OPENCL: OpenClAnalyticField})
return __implementations
[docs]
@classmethod
def default_implementation(cls):
return Implementation.PYTHON
@debug
def __init__(
self,
field,
formula,
variables,
extra_input_kwds=None,
implementation=None,
base_kwds=None,
**kwds,
):
"""
AnalyticField operator frontend.
Apply a user-defined formula onto a field, possibly
dependent on space variables and external fields/parameters.
Parameters
----------
field: hysop.field.continuous_field.Field
Continuous field to be modified.
formula : python function or sympy expression
The formula to be applied onto the field.
variables: dict
Dictionary of fields as keys and topology descriptors as values.
implementation: Implementation, optional, defaults to None
target implementation, should be contained in available_implementations().
If None, implementation will be set to default_implementation().
extra_input_kwds: dict, optional
Extra inputs that will be forwarded to the formula.
Fields and Parameters are handled correctly as input requirements.
Only used for Implementation.PYTHON, discarded for other implementations.
base_kwds: dict, optional
Base class keyword arguments.
kwds: dict, optional
Extra parameters passed towards operator implementation.
"""
check_instance(field, Field)
check_instance(variables, dict, keys=Field, values=CartesianTopologyDescriptors)
if implementation is Implementation.PYTHON:
kwds["extra_input_kwds"] = extra_input_kwds
super().__init__(
field=field,
formula=formula,
variables=variables,
implementation=implementation,
base_kwds=base_kwds,
**kwds,
)